package com.gdones.gpt.bus.gptstaff.service.impl;

import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.gdones.common.core.dto.MyPageBand;
import com.gdones.common.core.dto.MyPager;
import com.gdones.common.core.dto.MyResult;
import com.gdones.common.core.util.MyJWTUtil;
import com.gdones.gpt.bus.activecode.dto.ActiveCodeDto;
import com.gdones.gpt.bus.activecode.dto.GptActivationCode;
import com.gdones.gpt.bus.activecode.service.IGptActivationCodeService;
import com.gdones.gpt.bus.gptstaff.dto.*;
import com.gdones.gpt.bus.gptstaff.mapper.StaffInfoMapper;
import com.gdones.gpt.bus.gptstaff.mapper.StaffMapper;
import com.gdones.gpt.bus.gptstaff.mapper.StaffModeGrantMapper;
import com.gdones.gpt.bus.gptstaff.service.IStaffInfoService;
import com.gdones.gpt.bus.gptstaff.service.IStaffService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gdones.gpt.core.util.MyRedisUtil;
import com.wf.captcha.GifCaptcha;
import com.wf.captcha.base.Captcha;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author GaoJingBo
 * @since 2023-06-04
 */
@Service
@RefreshScope
@Transactional
public class StaffServiceImpl extends ServiceImpl<StaffMapper, Staff> implements IStaffService {

    @Autowired
    private JavaMailSender mailSender;

    @Value("${spring.mail.username}")
    private String myMailPath;

    @Resource(name = "staffInfoServiceImpl")
    private IStaffInfoService staffInfoServiceImpl;

    @Resource(name = "gptActivationCodeServiceImpl")
    private IGptActivationCodeService gptActivationCodeServiceImpl;

    @Autowired
    private StaffMapper staffMapper;

    @Autowired
    private StaffModeGrantMapper staffModeGrantMapper;

    @Autowired
    private MyRedisUtil redisUtil;

    @Autowired
    private StaffInfoMapper staffInfoMapper;


    private TimedCache<String, String> lruCache;

    {
        lruCache = CacheUtil.newTimedCache(DateUnit.MINUTE.getMillis() * 30);
    }

    private String registerCodeHtml = "<p>您好！<br/>感谢注册AilyChat，你的注册验证码：</p>\n" +
            "<p style=\"color:#FF4D00;font-size: 40px;\">XCODE</p>\n" +
            "<p>请在10分钟内使用验证码。<br/>为了你的账号安全，请不要把验证码泄露给其他人，谨防被骗。<br/>如果这不是你的请求，请忽略此邮件。</p>\n" +
            "<hr/>\n" +
            "<p>AilyChat</p>";

    private String resetPwdCodeHtml = "<p>您好！<br/>这是一封重置您AilyChat密码的验证邮件,如果不是您的操作请忽略，您的验证码：</p>\n" +
            "<p style=\"color:#FF4D00;font-size: 40px;\">XCODE</p>\n" +
            "<p>请在10分钟内使用验证码。<br/>为了你的账号安全，请不要把验证码泄露给其他人，谨防被骗。<br/>如果这不是你的请求，请忽略此邮件。</p>\n" +
            "<hr/>\n" +
            "<p>AilyChat</p>";

    @Override
    public MyResult registerSendReqCode( String mailTo,String localID) throws MessagingException {

        MyResult result = new MyResult();

        //长，宽，位数
        GifCaptcha gifCaptcha = new GifCaptcha(100, 42, 5);
        //设置类型
        gifCaptcha.setCharType(Captcha.TYPE_NUM_AND_UPPER);
        //生成验证码
        String code = gifCaptcha.text();

        System.err.println("--- 注册验证码【"+code+"】,发送邮箱："+myMailPath);
        System.err.println("注册验证1："+localID);
        lruCache.put("regssCode#"+localID,code,DateUnit.MINUTE.getMillis()*30);

        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setFrom(myMailPath);
        helper.setTo(mailTo);
        helper.setSubject("AilyChat-注册验证码");
        String newMsg = registerCodeHtml.replaceAll("XCODE", code);
        helper.setText(newMsg, true);
        mailSender.send(message);

        result.setMsg("验证码已发送到邮箱，请查收~");

        return result;
    }

    @Override
    public MyResult fgetSendReqCode( String mailTo,String localID) throws MessagingException {

        MyResult result = new MyResult();

        // 先看当前邮箱是否注册过账号
        QueryWrapper<Staff> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userName",mailTo);

        List<Staff> list = this.list(queryWrapper);
        if(list.size()<=0){
            result.setMsg("当前邮箱并未注册账号，请核查！");
            result.setCode("9999");
            return result;
        }

        //长，宽，位数
        GifCaptcha gifCaptcha = new GifCaptcha(100, 42, 5);
        //设置类型
        gifCaptcha.setCharType(Captcha.TYPE_NUM_AND_UPPER);
        //生成验证码
        String code = gifCaptcha.text();

        System.err.println("--- 忘记密码重置验证码【"+code+"】,发送邮箱："+myMailPath);
        lruCache.put("resetCode#"+localID,code, DateUnit.MINUTE.getMillis() * 30);

        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setFrom(myMailPath);
        helper.setTo(mailTo);
        helper.setSubject("AilyChat-密码重置验证码");
        String newMsg = resetPwdCodeHtml.replaceAll("XCODE", code);
        helper.setText(newMsg, true);
        mailSender.send(message);

        result.setMsg("验证码已发送到邮箱，请查收~");

        return result;
    }

    @Override
    public MyResult changePwd(Staff dto) {

        MyResult result = new MyResult();

        QueryWrapper<Staff> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userName",dto.getUserName());

        List<Staff> list = this.list(queryWrapper);

        Staff staff = list.get(0);
        String sm3Pwd = DigestUtil.digester("sm3").digestHex(dto.getPwd());
        staff.setPwd(sm3Pwd);
        boolean b = this.updateById(staff);
        if(b){
            result.setMsg("密码重置成功！");
        }else{
            result.setMsg("密码重置失败！");
            result.setCode("9999");
        }

        return result;
    }

    @Override
    public MyResult fgetCheck(String emailQrCode, String localID) {

        MyResult result = new MyResult();

        //1.验证码是否过期
        String resetCode = lruCache.get("resetCode#"+localID);
        if(resetCode==null){
            result.setMsg("验证码过期！");
            result.setCode("-9");
            return result;
        }
        // 2.验证验证码
        if(!resetCode.equalsIgnoreCase(emailQrCode)){
            result.setMsg("验证码输入错误！");
            result.setCode("9999");
            return result;
        }

        result.setMsg("验证成功！");

        return result;
    }

    @Override
    public MyResult getLoginReqCode(String localID) {

        MyResult result = new MyResult();

        //长，宽，位数
        GifCaptcha gifCaptcha = new GifCaptcha(100, 42, 5);
        //设置类型
        gifCaptcha.setCharType(Captcha.TYPE_NUM_AND_UPPER);
        //生成验证码
        String code = gifCaptcha.text();

        System.err.println("--- 登录验证码【"+code+"】");
        //redisUtil.set("loginReqCode#"+localID,code,60*10);
        lruCache.put("loginReqCode#"+localID,code,DateUnit.MINUTE.getMillis()*30);


        String base64 = gifCaptcha.toBase64();
        result.setMsg("登录验证码获取成功！");
        result.setData(base64);

        return result;
    }

    @Override
    public MyResult getStaffDataByID(String staffID) {

        MyResult result = new MyResult();

        StaffShowDto dto = new StaffShowDto();

        Staff staff = this.getById(staffID);

        // 核实会员是否到期
        Date now = new Date();
        Date vipEndTime = staff.getVipEndTime();
        int i = now.compareTo(vipEndTime);
        if(i>0){
            // 会员设置为过期无法使用
            staff.setVipIsCanUse("0");
            this.updateById(staff);

            boolean b = this.updateById(staff);
            StaffModeGrant updateDto = new StaffModeGrant();
            updateDto.setModeGrantUseTypeCode("3");
            updateDto.setModeGrantUseTypeName("未开通");
            updateDto.setUseLess(0);
            updateDto.setDayLimitCount(0);
            updateDto.setIsHaveDayLimit("0");

            QueryWrapper<StaffModeGrant> updateQuery = new QueryWrapper<>();
            updateQuery.eq("staffID",staff.getStaffID());
            int update = staffModeGrantMapper.update(updateDto, updateQuery);
        }

        List<StaffModeGrantDto> grants = staffMapper.getStaffModelGrantListByID(staffID);

        dto.setStaff(staff);
        dto.setGrantList(grants);

        result.setMsg("查询成功！");
        result.setData(dto);

        return result;
    }

    @Override
    public MyResult getStaffInfos(String staffID) {

        MyResult result = new MyResult();

        QueryWrapper<StaffInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("staffID",staffID);
        queryWrapper.orderByDesc("infoTime");

        List<StaffInfo> staffInfos = staffInfoMapper.selectList(queryWrapper);

        result.setMsg("记录查询成功！");
        result.setData(staffInfos);

        return result;
    }

    @Override
    public MyResult checkStaffCanUseModel(String staffModesID, String staffID) {

        MyResult result = new MyResult();

        // 先验证当前用户是否还可以继续调用
        Staff staff = this.getById(staffID);
        if("0".equals(staff.getVipIsCanUse())){
            result.setCode("9999");
            result.setMsg("账号会员状态过期无法,请续费后再使用！");
            return result;
        }

        // 验证调用账号是否过期
        // 核实会员是否到期
        Date now = new Date();
        Date vipEndTime = staff.getVipEndTime();
        int i = now.compareTo(vipEndTime);
        if(i>0){
            // 会员设置为过期无法使用
            staff.setVipIsCanUse("0");
            this.updateById(staff);

            boolean b = this.updateById(staff);
            StaffModeGrant updateDto = new StaffModeGrant();
            updateDto.setModeGrantUseTypeCode("3");
            updateDto.setModeGrantUseTypeName("未开通");
            updateDto.setUseLess(0);
            updateDto.setDayLimitCount(0);
            updateDto.setIsHaveDayLimit("0");

            QueryWrapper<StaffModeGrant> updateQuery = new QueryWrapper<>();
            updateQuery.eq("staffID",staff.getStaffID());
            int update = staffModeGrantMapper.update(updateDto, updateQuery);
            result.setCode("9999");
            result.setMsg("账号会员状态过期无法,请续费后再使用！");
            return result;
        }

        // 减少次数
        StaffModeGrant grant = staffModeGrantMapper.selectById(staffModesID);
        if("1".equals(grant.getIsHaveDayLimit())||grant.getDayLimitCount() != -1){
            int useLess = grant.getUseLess();
            if(useLess == 0){
                result.setCode("9999");
                result.setMsg("当前模型请求次数已用完！");
                return result;
            }
        }


        result.setMsg(null);

        return result;
    }

    @Override
    public MyResult updateStaffUseLess(String staffModesID) {

        MyResult result = new MyResult();

        StaffModeGrant grant = staffModeGrantMapper.selectById(staffModesID);
        int useLess = grant.getUseLess();
        if(useLess==0){
            result.setMsg("每日次数已用完，请明天再来或升级会员哟~");
            result.setCode("9999");
            result.setData(useLess);
            return result;
        }
        if("1".equals(grant.getIsHaveDayLimit())||grant.getDayLimitCount() != -1){
            useLess--;
            grant.setUseLess(useLess);
            int i = staffModeGrantMapper.updateById(grant);
            if(i<=0){
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            }
        }
        result.setMsg(null);
        result.setData(useLess);
        return result;
    }

    @Override
    public MyResult getStaffGrantByGrantID(String staffModesID) {

        MyResult result = new MyResult();

        StaffModeGrantDto dto = staffModeGrantMapper.getStaffGrantByGrantID(staffModesID);

        if(dto==null){
            result.setMsg("模型数据查询错误：未找到模型！");
            result.setCode("9999");
            return result;
        }
        result.setMsg(null);
        result.setData(dto);

        return result;
    }

    @Override
    public MyResult mypage(Staff dto, MyPageBand pageBand) {

        MyResult result = new MyResult();

        IPage<Staff> pages = staffMapper.pages(PageDTO.of(pageBand.getPage(), pageBand.getSize()), dto);

        // 根据分页的结果封装返回数据
        MyPager<Staff> pager = new MyPager<>();
        pager.setTotal(pages.getTotal());
        pager.setPage(pageBand.getPage());
        pager.setPageSize(pageBand.getSize());
        pager.setRows(pages.getRecords());

        result.setData(pager);
        result.setMsg("查询成功！");

        return result;
    }

    @Override
    public MyResult mylist(Staff dto) {

        MyResult result = new MyResult();

        List<Staff> list = staffMapper.myList(dto);

        result.setData(list);
        result.setMsg("查询成功！");

        return result;
    }

    @Override
    public MyResult myupdate(Staff dto) {

        MyResult result = new MyResult();

        boolean b = this.updateById(dto);
        if(b){
            result.setMsg("信息修改成功！");
        }else{
            result.setMsg("信息修改失败！");
            result.setCode("9999");
        }

        return result;
    }

    @Override
    public MyResult staffResetUseLess(String staffID) {

        MyResult result = new MyResult();

        staffMapper.staffResetUseLess(staffID);

        result.setMsg("重置完成！");

        return result;
    }

    @Override
    public MyResult deletes(String ids) {

        MyResult result = new MyResult();

        String[] split = ids.split(",");

        List<String> list = Arrays.asList(split);

        boolean b = this.removeBatchByIds(list);

        if(b){
            result.setMsg("删除成功！");
        }else{
            result.setMsg("删除失败！");
            result.setCode("9999");
        }

        return result;
    }

    @Override
    public MyResult getSimpleStaffData(String staffID) {

        MyResult result = new MyResult();

        Staff staff = this.getById(staffID);
        if(staff!=null){
            result.setMsg("查询成功！");
            result.setData(staff);
        }else{
            result.setMsg("查询失败！");
            result.setCode("9999");
        }


        return result;
    }

    @Override
    public MyResult updateStaffGrant(UpdateStaffGrantDto dto) {

        MyResult result = new MyResult();

        List<StaffModeGrant> grants = dto.getGrants();

        for(StaffModeGrant grant : grants){
            int i = staffModeGrantMapper.updateById(grant);
        }

        result.setMsg("授权信息修改成功！");

        return result;
    }

    @Override
    public MyResult add(StaffDto dto) {

        MyResult result = new MyResult();

        // 查询当前新增ip是否有注册过4次以上
        QueryWrapper<Staff> IPqueryWrapper = new QueryWrapper<>();
        IPqueryWrapper.eq("insertIP",dto.getInsertIP());
        List<Staff> ipstaffList = staffMapper.selectList(IPqueryWrapper);
        if(ipstaffList.size()>=4){
            result.setMsg("您注册的账号已达上线，无法再注册~感谢支持");
            result.setCode("9999");
            return result;
        }

        // 查询出当前注册的客户信息
        QueryWrapper<Staff> queryWrapper = new QueryWrapper<>();
        List<Staff> staffList = staffMapper.selectList(queryWrapper);

        // 1.先判断是否重复注册
        boolean isCf = false;
        for(Staff temp:staffList){
            if(temp.getUserName().equals(dto.getUserName())){
                isCf = true;
                break;
            }
        }

        if(isCf){
            result.setMsg("当前邮箱已注册过账号,您可以尝试登录。");
            result.setCode("9999");
            return result;
        }

        // 2.判断昵称是否重复
        boolean isNickCf = false;
        for(Staff temp2:staffList){
            if(temp2.getNikeName().equals(dto.getNikeName())){
                isNickCf = true;
                break;
            }
        }

        if(isNickCf){
            result.setMsg("昵称重复，您可以换一个名字。");
            result.setCode("9999");
            return result;
        }

        // 3. 验证验证码是否失效
        System.err.println("注册验证："+dto.getLocalID());
        String regCode = lruCache.get("regssCode#"+dto.getLocalID());
        if(regCode==null){
            result.setMsg("验证码失效,请重新输入验证码");
            result.setCode("-9");
            return result;
        }

        // 4. 验证验证码
        if(!(regCode.equalsIgnoreCase(dto.getEmailQrCode()))){
            result.setMsg("验证码错误，请重新输入。");
            result.setCode("9999");
            return result;
        }

        // 5. 账号添加
        Staff addStaff = new Staff();
        BeanUtils.copyProperties(dto,addStaff);
        addStaff.setStaffID(IdUtil.fastSimpleUUID());
        // 加密密码
        String sm3Pwd = DigestUtil.digester("sm3").digestHex(dto.getPwd());
        addStaff.setPwd(sm3Pwd);

        int insert = staffMapper.insert(addStaff);

        if(insert<=0){
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            result.setMsg("注册功能异常，请联系管理员。");
            result.setCode("9999");
            return result;
        }

        //6. 账号激活判断
        if(dto.getActivationCode()==null){// 体验用户

            result = gptActivationCodeServiceImpl.staffActiveTiYan(addStaff.getStaffID());

        }else{// 有激活码但是要验证

            GptActivationCode acDto = new ActiveCodeDto();
            acDto.setStaffID(addStaff.getStaffID());
            acDto.setStaffName(addStaff.getUserName());
            acDto.setActiveCode(dto.getActivationCode());

            result = gptActivationCodeServiceImpl.staffActiveTheCode(acDto);
        }

        if("0000".equals(result.getCode())){// 激活成功
            result.setMsg("账号注册成功！"+result.getMsg());
        }else{// 激活失败
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            result.setMsg(result.getMsg());
        }

        return result;
    }

    @Override
    public MyResult login(HttpServletRequest request,StaffDto dto) {

        MyResult result = new MyResult();

        // 验证客户端是否锁定
        Object pclock = redisUtil.get("pclock#"+dto.getLocalID());
        if(pclock!=null){
            result.setMsg("当前客户端尝试密码错误次数过多已锁定请30分钟后再试!");
            result.setCode("9999");
            return result;
        }

        // 1.判断验证码是否过期
        String loginReqCode = lruCache.get("loginReqCode#" + dto.getLocalID());
        if(loginReqCode==null){
            result.setMsg("验证码过期!");
            result.setCode("-9");
            return result;
        }
        // 2.验证验证码
        String loginReqCodeStr = (String) loginReqCode;
        if(!(loginReqCodeStr.equalsIgnoreCase(dto.getQrCode()))){
            result.setMsg("验证码输入错误!");
            result.setCode("9999");
            return result;
        }
        // 3. 账号密码验证
        QueryWrapper<Staff> loginQuery = new QueryWrapper<>();
        loginQuery.eq("userName",dto.getUserName());

        List<Staff> list = this.list(loginQuery);
        if(list.size()<=0){ // 用户名不存在

            result = loginErrConting(dto);
            return result;
        }
        // 密码验证
        Staff staff = list.get(0);
        // 输入密码加密
        String sm3Pwd = DigestUtil.digester("sm3").digestHex(dto.getPwd());
        if(!staff.getPwd().equals(sm3Pwd)){
            staffInfoServiceImpl.logForLogin(request,staff.getStaffID(),staff.getUserName(),false);
            result = loginErrConting(dto);
            return result;
        }
        // 登录成功获取当前用户的授权信息
        //Object staffData = this.getStaffDataByID(staff.getStaffID()).getData();
        // 登录成功，生成token
        //登录成功需要放置两个信息
        // user: 登录成功的当前用户实体信息
        // token: jwt令牌
        Map<String,Object> res = new HashMap<>();
        res.put("staff",staff.getStaffID());
        res.put("token", MyJWTUtil.getToken(dto.getUserName(),"{name:'"+dto.getUserName()+"'}"));

        staffInfoServiceImpl.logForLogin(request,staff.getStaffID(),staff.getUserName(),true);

        result.setMsg("登录成功！");
        result.setData(res);

        return result;
    }

    private MyResult loginErrConting( StaffDto dto) {

        MyResult result = new MyResult();
        result.setMsg("用户名或密码错误！");
        result.setCode("9999");

        Object locktime = redisUtil.get("locktime#"+dto.getLocalID());
        if(locktime==null){
            redisUtil.set("locktime#"+dto.getLocalID(),1,60*30);
        }else{
            int locktimeInt = (int)locktime;
            locktimeInt++;
            if(locktimeInt>30){
                redisUtil.set("pclock#"+dto.getLocalID(),true,60*30);
                result.setMsg("当前客户端尝试密码错误次数过多已锁定请30分钟后再试!");
                result.setCode("9999");
            }
            redisUtil.set("locktime#"+dto.getLocalID(),locktimeInt,60*30);
        }
        return result;
    }
}
